package org.example.controller;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import org.example.pojo.Result;
import org.example.pojo.SeckillActivity;
import org.example.pojo.SeckillOrder;
import org.example.service.SeckillActivityService;
import org.example.service.SeckillOrderService;
import org.example.utils.RedisIdWorker;
import org.example.utils.RedisLock;
import org.mockito.internal.matchers.Or;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.Date;
import java.util.concurrent.ConcurrentHashMap;

@RestController
@RequestMapping("coupon")
public class CouponController 
{
	/**
	 * 生成优惠卷
	 */
	@PostMapping("createCoupon")
	public void createCoupon()
	{
		
	}
	
	@Autowired
	SeckillActivityService seckillActivityService;
	
	@Autowired
	SeckillOrderService seckillOrderService;
	
	//ID生成器
	@Autowired
	RedisIdWorker redisIdWorker;

	/**
	 * 生成优惠卷订单
	 */
	//在多并发情况下出现超卖情况
	@Transactional
	@GetMapping("seckillCoupon")
	public Result seckillCoupon(@RequestParam Long coupon_id,@RequestParam Long uid)
	{
		//查询活动信息
		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		if(one == null)
		{
			throw new RuntimeException("活动不存在");
		}
		
		LocalDateTime now = LocalDateTime.now();//当前时间
		//判断秒杀活动是否开始
		if(now.isBefore(one.getStartTime()))
		{
			throw new RuntimeException("活动未开始");
		}
		//判断秒杀活动是否结束
		if(now.isAfter(one.getEndTime()))
		{
			throw new RuntimeException("活动已结束");
		}
		
		//判断库存是否充足
		Integer totalStock = one.getTotalStock();
		if(totalStock < 0)
		{
			throw  new RuntimeException("库存不足");
		}
		//扣减库存
		one.setTotalStock(totalStock-1);
		seckillActivityService.updateById(one);
		
		//创建订单
		SeckillOrder seckillOrder = new SeckillOrder();
		seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
		seckillOrder.setUserId(uid);
		seckillOrder.setActivityId(one.getId());
		seckillOrder.setCouponId(coupon_id);
		
		seckillOrderService.save(seckillOrder);
		
		return Result.ok();
	}

	/**
	 * 处理超卖情况，乐观锁,使用版本号机制cas
	 * 没有处理掉一人一单的情况
	 */
	@Transactional
	@GetMapping("seckillCouponCas")
	public Result seckillCouponCas(@RequestParam Long coupon_id,@RequestParam Long uid)
	{
		//查询活动信息
		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		if(one == null)
		{
			throw new RuntimeException("活动不存在");
		}

		LocalDateTime now = LocalDateTime.now();//当前时间
		//判断秒杀活动是否开始
		if(now.isBefore(one.getStartTime()))
		{
			throw new RuntimeException("活动未开始");
		}
		//判断秒杀活动是否结束
		if(now.isAfter(one.getEndTime()))
		{
			throw new RuntimeException("活动已结束");
		}

		//判断库存是否充足
		Integer totalStock = one.getTotalStock();
		if(totalStock < 0)
		{
			throw  new RuntimeException("库存不足");
		}
		//一人一单处理，禁止黄牛行为
		//查询订单表，如果存在就不卖给他
		//在高并发情况下还是会出问题
		QueryWrapper oderQW = new QueryWrapper();
		oderQW.eq("coupon_id",coupon_id);
		oderQW.eq("user_id",uid);
		if( seckillOrderService.count(oderQW) > 0)
		{
			throw new RuntimeException("已抢到");
		}
		
		/**
		 *	乐观锁处理部分
		 */
		//扣减库存
		//这样写效率不高，因为根据了之前的信息必须保持一致才能修改
//		qw.eq("total_stock",totalStock);
//		one.setTotalStock(totalStock-1);
//		boolean update = seckillActivityService.update(one, qw);
		//改进版本，只要库存大于0就能修改
		boolean update = seckillActivityService.update().setSql("total_stock = total_stock-1")//必须获取数据库里面的total_stock值-1
				.eq("coupon_id",coupon_id)
				.gt("total_stock",0)
				.update();//执行

		if(update) {
			//创建订单
			SeckillOrder seckillOrder = new SeckillOrder();
			seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
			seckillOrder.setUserId(uid);
			seckillOrder.setActivityId(one.getId());
			seckillOrder.setCouponId(coupon_id);

			seckillOrderService.save(seckillOrder);
			return Result.ok();
		}
			return Result.error();
	}

	/**
	 *	处理高并发情况的一人一单问题，上悲观锁
	 */
//	@Transactional	//在controller层这个事务会导致synchronized锁失效
	@GetMapping("seckillCouponCasSynchronized")
	public Result seckillCouponCasSynchronized(@RequestParam Long coupon_id,@RequestParam Long uid)
	{
		//查询活动信息
		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		if(one == null)
		{
			throw new RuntimeException("活动不存在");
		}

		LocalDateTime now = LocalDateTime.now();//当前时间
		//判断秒杀活动是否开始
		if(now.isBefore(one.getStartTime()))
		{
			throw new RuntimeException("活动未开始");
		}
		//判断秒杀活动是否结束
		if(now.isAfter(one.getEndTime()))
		{
			throw new RuntimeException("活动已结束");
		}

		//判断库存是否充足
		Integer totalStock = one.getTotalStock();
		if(totalStock < 0)
		{
			throw  new RuntimeException("库存不足");
		}
		//一人一单处理，禁止黄牛行为
		//查询订单表，如果存在就不卖给他
		//在高并发情况下还是会出问题
		//不能直接拿LONG做为锁，在128以下的值是可以锁住，因为是128以下是从常量池取出来的，超过128会new一个新对象
		synchronized (uid.toString().intern()) //intern()从常量池找，找不到再创建对象
		{
			
		QueryWrapper oderQW = new QueryWrapper();
		oderQW.eq("coupon_id",coupon_id);
		oderQW.eq("user_id",uid);
		if( seckillOrderService.count(oderQW) > 0)
		{
			throw new RuntimeException("已抢到");
		}

		/**
		 *	乐观锁处理部分
		 */
		//扣减库存
		//这样写效率不高，因为根据了之前的信息必须保持一致才能修改
//		qw.eq("total_stock",totalStock);
//		one.setTotalStock(totalStock-1);
//		boolean update = seckillActivityService.update(one, qw);
		//改进版本，只要库存大于0就能修改

			boolean update = seckillActivityService.update().setSql("total_stock = total_stock-1")//必须获取数据库里面的total_stock值-1
					.eq("coupon_id", coupon_id)
					.gt("total_stock", 0)
					.update();//执行

			if (update) {
				//创建订单
				SeckillOrder seckillOrder = new SeckillOrder();
				seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
				seckillOrder.setUserId(uid);
				seckillOrder.setActivityId(one.getId());
				seckillOrder.setCouponId(coupon_id);

				seckillOrderService.save(seckillOrder);
				return Result.ok();
			}
			return Result.error();
		}
	}
	
	@Autowired
	RedisTemplate redisTemplate;

	/**
	 * 集群模式解决方案，使用redis互斥锁
	 */
	@GetMapping("seckillCouponRedis")
	public Result seckillCouponRedis(@RequestParam Long coupon_id,@RequestParam Long uid)
	{
		//查询活动信息
		QueryWrapper qw = new QueryWrapper();
		qw.eq("coupon_id",coupon_id);
		SeckillActivity one = seckillActivityService.getOne(qw);
		if(one == null)
		{
			throw new RuntimeException("活动不存在");
		}

		LocalDateTime now = LocalDateTime.now();//当前时间
		//判断秒杀活动是否开始
		if(now.isBefore(one.getStartTime()))
		{
			throw new RuntimeException("活动未开始");
		}
		//判断秒杀活动是否结束
		if(now.isAfter(one.getEndTime()))
		{
			throw new RuntimeException("活动已结束");
		}

		//判断库存是否充足
		Integer totalStock = one.getTotalStock();
		if(totalStock < 0)
		{
			throw  new RuntimeException("库存不足");
		}
		//一人一单处理，禁止黄牛行为
		//创建锁
		RedisLock redisLock = new RedisLock("coupon", redisTemplate);
		//上锁，失败则返回错误
		if(!redisLock.locked(30)) 
		{
			return Result.error();
		}
		try 
		{
			QueryWrapper oderQW = new QueryWrapper();
			oderQW.eq("coupon_id", coupon_id);
			oderQW.eq("user_id", uid);
			if (seckillOrderService.count(oderQW) > 0) {
				throw new RuntimeException("已抢到");
			}

			/**
			 *	乐观锁处理部分
			 */
			boolean update = seckillActivityService.update().setSql("total_stock = total_stock-1")//必须获取数据库里面的total_stock值-1
					.eq("coupon_id", coupon_id)
					.gt("total_stock", 0)
					.update();//执行
			if (update)
			{
				//创建订单
				SeckillOrder seckillOrder = new SeckillOrder();
				seckillOrder.setOrderSn(redisIdWorker.nextId("coupon"));
				seckillOrder.setUserId(uid);
				seckillOrder.setActivityId(one.getId());
				seckillOrder.setCouponId(coupon_id);

				seckillOrderService.save(seckillOrder);
				return Result.ok();
			}		
			return Result.error();
		}
		finally {
			redisLock.unlock();
		}
	}//***********
	
}/////////////////
